#!/usr/bin/env python

import time
import sys
import Optical_Receiver as receiver

receiver.init()
test_mode = False

# Dicitionary of reverse Morse codes for: ( ) + , - . / 0 ... 9 : ; = ? @ A ... Z
reverse_code = {'31331':'(','313313':')','13131':'+','331133':',','311113': '-','131313':'.','31131':'/','33333':'0','13333':'1','11333':'2','11133':'3','11113':'4','11111':'5','31111':'6','33111':'7','33311':'8','33331':'9','333111':':','313131':';','31113':'=','113311':'?','133131':'@','13':'A','3111':'B','3131':'C','311':'D','1':'E','1131':'F','331':'G','1111':'H','11':'I','1333':'J','313':'K','1311':'L','33':'M','31':'N','333':'O','1331':'P','3313':'Q','131':'R','111':'S','3':'T','113':'U','1113':'V','133':'W','3113':'X','3133':'Y','3311':'Z'}

def get_DIT_duration():
  state = True
  prev_state = True
  slopes = 1

  # waiting for first rising slope
  while not receiver.get_state():
    time.sleep(0.0001)

  synchronization_start = time.time()

  print ('Synchronization in progress...')

  # start of synchronization
  while slopes < 10:
    state = receiver.get_state()
    
    # rising slope
    if (state and not prev_state):
      slopes = slopes + 1
      
    # falling slope
    elif (not state and prev_state):
      slopes = slopes + 1
    
    prev_state = state
    
  synchronization_end = time.time()
  
  print ('\nSynchronization completed')
  return(synchronization_end-synchronization_start)/(slopes-1)


print ('*****   Morse Code Receiver   *****')
print ('\nWaiting for synchronization...\n')

if test_mode:
  DIT = 0.02
else:
  DIT = get_DIT_duration()

print ('\nReceiving at ' + str(round(1/DIT)) + ' Dits/s.')
print ('\n***** Start of Transmission *****\n')

time.sleep(0.5)

# waiting for first rising slope of data
while not receiver.get_state():
  time.sleep(0.0001)

rise_time = time.time()
fall_time = time.time()
transmission_start = time.time()
received_code = ''
end_of_transmission = False
state = True
prev_state = True
print_index = 1
char_received = 0

while not end_of_transmission:
  # transmission ends, when break is longer than 15 DITs
  if not state and (time.time() - fall_time) > (15*DIT) and not test_mode:
    # Test_Receiver: comment following instruction out
    end_of_transmission = True
    # print last received letter
    state = True
    prev_state = False
    
  # rising slope detected, calculate length of break, print character
  if (state and not prev_state):
    rise_time = time.time()
    length = rise_time - fall_time    

    # break between two letters or two words
    if round(length/DIT) > 1:
      if received_code in reverse_code:
        char_received = char_received + 1
        sys.stdout.write(reverse_code[received_code])
        sys.stdout.flush()
        print_index = print_index + 1
        
        # break between two words print space character
        if round(length/DIT) > 5:
          char_received = char_received + 1
          sys.stdout.write(' ')
          sys.stdout.flush()
          print_index = print_index + 1
          
          # new line
          if print_index > 65:
            print()
            print_index = 1
      else:
        print('\nTransmission Error: ' + received_code + ' not in Morse code!')
        print_index = 1
      received_code = ''

  # falling slope detected, calculate length of pulse, add symbol to received_code
  elif (not state and prev_state):
    fall_time = time.time()
    length = fall_time - rise_time
    
    # add dit or dah to received code
    if round(length/DIT) == 1:
      received_code = received_code + '1'
    else:
      received_code = received_code + '3'
      
  # reduce CPU load
  time.sleep(0.001)
  prev_state = state
  state = receiver.get_state()

# transmission ended 15-3 = 12 DITs before now
transmission_end = time.time() - 12 * DIT

print ('\n\n*****  End of Transmission  ******\n')  
print (str(char_received-1) + ' characters received in ' + str(round(transmission_end - transmission_start,2)) + ' s.')
print ('\n**********************************')
